package drds.plus.sql_process.parser;

import drds.plus.common.model.SqlType;
import drds.plus.parser.abstract_syntax_tree.statement.*;
import drds.plus.parser.abstract_syntax_tree.statement.select.table.Tables;
import drds.plus.parser.visitor.Visitor;
import drds.plus.sql_process.abstract_syntax_tree.node.Node;
import drds.plus.sql_process.abstract_syntax_tree.node.dml.Delete;
import drds.plus.sql_process.abstract_syntax_tree.node.dml.Insert;
import drds.plus.sql_process.abstract_syntax_tree.node.dml.Replace;
import drds.plus.sql_process.abstract_syntax_tree.node.dml.Update;
import drds.plus.sql_process.abstract_syntax_tree.node.query.Query;
import drds.plus.sql_process.parser.visitor.TableNameVisitor;
import drds.plus.sql_process.parser.visitor.dml.*;

import java.util.Set;

public class ParseInfoImpl implements ParseInfo {

    private SqlType sqlType;
    private Visitor visitor;
    private Statement statement;
    private boolean hasVisited;
    private String sql;

    private String parameterizedSql;

    public void build(String sql, Statement statement, String parameterizedSql) {
        if (sql != null) {
            this.sql = sql;
            this.parameterizedSql = parameterizedSql;
            this.statement = statement;

            if (statement instanceof SelectStatement) {


                if (isEmptyTable((SelectStatement) statement)) {
                    sqlType = SqlType.SELECT_WITHOUT_TABLE;
                } else {
                    sqlType = SqlType.SELECT;
                    visitor = new SelectVisitor();
                }
            } else if (statement instanceof InsertStatement) {
                sqlType = SqlType.INSERT;
                visitor = new InsertVisitor();
            } else if (statement instanceof ReplaceStatement) {
                sqlType = SqlType.REPLACE;
                visitor = new ReplaceVisitor();
            } else if (statement instanceof UpdateStatement) {
                sqlType = SqlType.UPDATE;
                visitor = new UpdateVisitor();
            } else if (statement instanceof DeleteStatement) {
                sqlType = SqlType.DELETE;
                visitor = new DeleteVisitor();
            } else {
                throw new IllegalArgumentException("not support  statement:'" + sql + "'");
            }
        }
    }

    public void accept() {
        if (hasVisited == false && statement != null && visitor != null) {
            statement.accept(visitor);
            hasVisited = true;
        }
    }

    public SqlType getSqlType() {
        return sqlType;
    }


    public Insert getInsertNode() {
        accept();
        return ((InsertVisitor) visitor).getInsertNode();
    }

    public Replace getReplaceNode() {
        accept();
        return ((ReplaceVisitor) visitor).getReplaceNode();
    }

    public Update getUpdateNode() {
        accept();
        return ((UpdateVisitor) visitor).getUpdateNode();
    }

    public Delete getDeleteNode() {
        accept();
        return ((DeleteVisitor) visitor).getDeleteNode();
    }

    public Query getQueryNode() {
        accept();
        return ((SelectVisitor) visitor).getQuery();
    }

    public Node getNode() {
        if (sqlType == SqlType.SELECT || sqlType == SqlType.SELECT_LAST_INSERT_ID || sqlType == SqlType.SELECT_WITHOUT_TABLE) {
            return getQueryNode();
        } else if (sqlType == SqlType.INSERT) {
            return getInsertNode();
        } else if (sqlType == SqlType.REPLACE) {
            return getReplaceNode();
        } else if (sqlType == SqlType.UPDATE) {
            return getUpdateNode();
        } else if (sqlType == SqlType.DELETE) {
            return getDeleteNode();
        }

        throw new UnsupportedOperationException(sqlType.toString());
    }

    public Statement getStatement() {
        return statement;
    }

    public String getSql() {
        return this.sql;
    }

    public boolean isAbstractSyntaxTreeNode() {
        return !(sqlType == SqlType.PROCEDURE || sqlType == SqlType.SHOW || sqlType == SqlType.SELECT_WITHOUT_TABLE || sqlType == SqlType.SET);
    }

    public Set<String> getTableNameToSchemaMap() {
        TableNameVisitor tableNameVisitor = new TableNameVisitor();
        statement.accept(tableNameVisitor);
        return tableNameVisitor.getTableNameSet();
    }

    public String getParameterizedSql() {
        return parameterizedSql;
    }


    private boolean isEmptyTable(SelectStatement selectStatement) {
        Tables tables = selectStatement.getTables();
        return tables == null;
    }


}
